package com.jh.wdbc.service;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.jh.wdbc.model.ConnectionInfo;
import com.jh.wdbc.model.MapList;
import com.nhncorp.dbpropmanager.model.PropertyModel;
import com.nhnent.dbpmdecryptor.DataSourcePropertyEncryptor;

public class ConnectionManager {

	public static final long TIMEOUT = 60000;
	private static DataSourcePropertyEncryptor encryptor = new DataSourcePropertyEncryptor();
	public static MapList<ConnectionInfo> connections = new MapList<ConnectionInfo>();

	public static Connection createConnection(String dbType, String url, String user, String passwd) throws ClassNotFoundException, SQLException {
		Class.forName(dbType);
		return DriverManager.getConnection(url, user, passwd);
	}

	public static ConnectionInfo createConnectionInfo(String name) throws Exception {
		String names[] = name.split("\\.");
		StringBuilder sb = new StringBuilder(names[1]);
		for (int i = 2; i < names.length; i++) {
			sb.append(".").append(names[i]);
		}

		ConnectionInfo connectionInfo = getConnectionInfoFromDBPM(names[0], sb.toString());
		if (connectionInfo == null) {
			return null;
		}

		connectionInfo.setConnection(createConnection(connectionInfo.getDbType(), connectionInfo.getUrl(), connectionInfo.getUser(), connectionInfo.getPasswd()));

		return connectionInfo;
	}

	public static ConnectionInfo getConnectionInfoFromDBPM(String dbName) throws Exception {
		try {
			String names[] = dbName.split("\\.");
			StringBuilder sb = new StringBuilder(names[1]);
			for (int i = 2; i < names.length; i++) {
				sb.append(".").append(names[i]);
			}
			return getConnectionInfoFromDBPM(names[0], sb.toString());

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public static ConnectionInfo getConnectionInfoFromDBPM(String phase, String dbid) throws Exception {
		try {
			String dbName = phase + "." + dbid;

			PropertyModel dbpm = DBPMUtil.getDBProperty(phase, dbid);
			String driver = dbpm.getDriver();
			String url = dbpm.getUrl();
			String username = dbpm.getUsername();
			String passwd = dbpm.getPassword();

			url = encryptor.decrypt(url);
			username = encryptor.decrypt(username);
			passwd = encryptor.decrypt(passwd);

			ConnectionInfo connectionInfo = new ConnectionInfo();

			connectionInfo.setName(dbName);
			connectionInfo.setDbType(driver);
			connectionInfo.setUser(username);
			connectionInfo.setPasswd(passwd);
			connectionInfo.setUrl(url);
			connectionInfo.setRawEncoding(dbpm.getEncoding());

			return connectionInfo;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public static ConnectionInfo getConnection(String name) throws Exception {
		List<ConnectionInfo> connectionList = connections.getListForKey(name);
		ConnectionInfo returnConnection = null;
		if (connectionList != null) {
			for (ConnectionInfo info : connectionList) {
				if (!info.inUse) {
					returnConnection = info;
				}
			}
		}

		if (returnConnection == null) {
			returnConnection = createConnectionInfo(name);
			connections.add(returnConnection);
		}
		
		returnConnection.inUse = true;

		return returnConnection;
	}

	public static void releaseConnection(ConnectionInfo connection) throws SQLException {
		List<ConnectionInfo> connectionList = connections.getListForKey(connection.getName());
		
		if(connectionList == null){
			return;
		}
		
		if (connectionList.size() > 2) {
			closeConnection(connection);
		} else {
			connection.inUse = false;
		}
		connection.timestamp = (new Date()).getTime();
	}

	public static void closeConnection(ConnectionInfo connection) throws SQLException {
		connection.getConnection().close();
		connections.remove(connection);
	}

	public static void reconect(ConnectionInfo connectionInfo) throws SQLException, Exception {
		connectionInfo.getConnection().close();
		connectionInfo.setConnection(createConnection(connectionInfo.getDbType(), connectionInfo.getUrl(), connectionInfo.getUser(), connectionInfo.getPasswd()));
	}

	public static void destory() throws Exception {
		for (ConnectionInfo info : connections) {
			closeConnection(info);
		}
	}

	public static void clear() throws SQLException {
		List<ConnectionInfo> removeList = new ArrayList<ConnectionInfo>();
		long now = (new Date()).getTime();
		for (ConnectionInfo info : connections) {
			if (now - info.timestamp > TIMEOUT && !info.inUse) {
				info.inUse = true;
				info.getConnection().close();
				removeList.add(info);
			}
		}
		for (ConnectionInfo info : removeList) {
			connections.remove(info);
		}
		removeList.removeAll(removeList);
	}

}
